<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="tiH2x" id="tiH2x"><span data-lake-id="u95a4e42c" id="u95a4e42c">典型回答</span></h1>
  <p data-lake-id="u50057f4b" id="u50057f4b"><br></p>
  <p data-lake-id="u75da30c7" id="u75da30c7"><br></p>
  <p data-lake-id="u8aaa580c" id="u8aaa580c"><span data-lake-id="u21f4c4c5" id="u21f4c4c5">字符串常量池中的常量有两种来源，一种是字面量会在编译期先进入到Class常量池，然后再在运行期进去到字符串池，还有一种就是在运行期通过intern将字符串对象手动添加到字符串常量池中。</span></p>
  <p data-lake-id="u70297df8" id="u70297df8"><br></p>
  <p data-lake-id="u4611cd91" id="u4611cd91"><span data-lake-id="u6382897e" id="u6382897e">那么，Class常量池中的常量，是在什么时候被放进到字符串池的呢？</span></p>
  <p data-lake-id="u46c3a01c" id="u46c3a01c"><br></p>
  <p data-lake-id="u0385cfbc" id="u0385cfbc"><span data-lake-id="u232a982a" id="u232a982a">Java 的类加载过程要经历加载（Loading）、链接（Linking）、初始化（Initializing）等几个步骤，在链接这个步骤，又分为验证（Verification）、准备（Preparation）以及解析（Resolution）等几个步骤。</span></p>
  <p data-lake-id="uc0b1cf1c" id="uc0b1cf1c"><br></p>
  <p data-lake-id="u1ec87eca" id="u1ec87eca"><span data-lake-id="u0879328e" id="u0879328e">在 Java 虚拟机规范及 Java语言规范中都提到过：</span></p>
  <p data-lake-id="ua8978fae" id="ua8978fae"><br></p>
  <blockquote data-lake-id="ua5a34bc7" id="ua5a34bc7">
   <p data-lake-id="uf7a8a6a3" id="uf7a8a6a3"><span data-lake-id="u3630d05a" id="u3630d05a">《The Java Virtual Machine Specification》 5.4 Linking：</span></p>
   <p data-lake-id="uc44fd5d4" id="uc44fd5d4"><span data-lake-id="u0e405b68" id="u0e405b68"> </span></p>
   <p data-lake-id="u291e606a" id="u291e606a"><span data-lake-id="u7ee6e27f" id="u7ee6e27f">For example, a Java Virtual Machine implementation may choose to resolve each symbolic reference in a class or interface individually when it is used ("lazy" or "late" resolution), or to resolve them all at once when the class is being verified ("eager" or "static" resolution)</span></p>
   <p data-lake-id="u9279b0b1" id="u9279b0b1"><span data-lake-id="ub75b006d" id="ub75b006d"> </span></p>
   <p data-lake-id="uec4af44a" id="uec4af44a"><span data-lake-id="ub386af5e" id="ub386af5e">《The Java Language Specification》 12.3 Linking of Classes and Interfaces</span></p>
   <p data-lake-id="uc56284c8" id="uc56284c8"><span data-lake-id="u6ba05f53" id="u6ba05f53"> </span></p>
   <p data-lake-id="u04413b13" id="u04413b13"><span data-lake-id="u86f4d211" id="u86f4d211">For example, an implementation may choose to resolve each symbolic reference in a class or interface individually, only when it is used (lazy or late resolution), or to resolve them all at once while the class is being verified (static resolution). This means that the resolution process may continue, in some implementations, after a class or interface has been initialized.</span></p>
  </blockquote>
  <p data-lake-id="u5e46fb35" id="u5e46fb35"><br></p>
  <p data-lake-id="ucfdf3e3d" id="ucfdf3e3d"><span data-lake-id="ubab7ca3d" id="ubab7ca3d">大致意思差不多，就是说，Java 虚拟机的实现可以选择只有在用到类或者接口中的符号引用时才去逐一解析他（延迟解析），或者在验证类的时候就解析每个引用（预先解析）。这意味着在一些虚拟机实现中，把常量放到常量池的步骤可能是</span><strong><span data-lake-id="u56123c5d" id="u56123c5d">延迟处理</span></strong><span data-lake-id="u1ab71fc2" id="u1ab71fc2">的。</span></p>
  <p data-lake-id="u33e6dc83" id="u33e6dc83"><br></p>
  <p data-lake-id="u20934db7" id="u20934db7"><span data-lake-id="u95a7d3a7" id="u95a7d3a7">对于 HotSpot 虚拟机来说，字符串字面量，和其他基本类型的常量不同，并不会在类加载中的解析阶段填充并驻留在字符串常量池中，而是以特殊的形式存储在运行时常量池中。</span><strong><span data-lake-id="u4be71452" id="u4be71452">只有当这个字符串字面量被调用时，才会对其进行解析，开始为他在字符串常量池中创建对应的 String 实例。</span></strong></p>
  <p data-lake-id="u81d4133c" id="u81d4133c"><br></p>
  <p data-lake-id="u2a3a8ee9" id="u2a3a8ee9"><span data-lake-id="u3fe27a9e" id="u3fe27a9e">通过查看 HotSpot JDK 1.8 的 ldc 指令的源代码，也可以验证上面的说法。</span></p>
  <p data-lake-id="u74626c52" id="u74626c52"><br></p>
  <blockquote data-lake-id="uc7eddc98" id="uc7eddc98">
   <p data-lake-id="u2a200ae4" id="u2a200ae4"><span data-lake-id="u194b9fe6" id="u194b9fe6">ldc 指令表示int、float或String型常量从常量池推送至栈顶</span></p>
  </blockquote>
  <p data-lake-id="uabf0e5e7" id="uabf0e5e7"><br></p>
  <pre lang="java"><code>
IRT_ENTRY(void, InterpreterRuntime::ldc(JavaThread* thread, bool wide))
  // access constant pool
  ConstantPool* pool = method(thread)-&gt;constants();
  int index = wide ? get_index_u2(thread, Bytecodes::_ldc_w) : get_index_u1(thread, Bytecodes::_ldc);
  constantTag tag = pool-&gt;tag_at(index);

  assert (tag.is_unresolved_klass() || tag.is_klass(), "wrong ldc call");
  Klass* klass = pool-&gt;klass_at(index, CHECK);
    oop java_class = klass-&gt;java_mirror();
    thread-&gt;set_vm_result(java_class);
IRT_END
</code></pre>
  <p data-lake-id="u945a78d7" id="u945a78d7"><br></p>
  <p data-lake-id="u8c1b0a06" id="u8c1b0a06"><br></p>
  <p data-lake-id="u1ef39f0e" id="u1ef39f0e"><span data-lake-id="u38051232" id="u38051232">所以，字符串常量，是在第一次被调用(准确的说是ldc指令)的时候，进行解析并在字符串池中创建对应的String实例的。</span></p>
 </body>
</html>